package org.spring.springboot.service.impl;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/** 
* @author 作者 zhujf: 
* @version 创建时间：2017年8月31日 上午9:04:24 
* 
*/
@RestController
@RequestMapping("/use")
public class Useing {

	 @Autowired
	 private StringRedisTemplate stringRedisTemplate;
	
	
	 /**
	  * 场景一：简单计数功能
	  * Redis是一个很好的计数器，计数器是 Redis 的原子性自增操作可实现的最直观的模式了，它的想法相当简单：每当某个操作发生时，
	  * 向 Redis 发送一个 INCR 命令。使用场景比如网站的访问数，注册用户数，文章的点赞数，高并发的秒杀活动，
	  * 分布式序列号生成等等统计计数的功能实现。Redis 解决这类计数问题得心应手，相比关系数据库速度更快，消耗资源更少。还可以通过set()方法来重置计数。
	  * @return
	  */
	 
	@RequestMapping("/count")
	public String countNum(){
		ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();  
		
		
		//计数前打印
		System.out.println(opsForValue.get("test1"));
		for (int i = 0; i < 100; i++) {
			//计数，第一个参数为key值，第二个参数为每次增加计数的单位
			opsForValue.increment("test1", 1);
		}
		//计数后打印
		System.out.println(opsForValue.get("test1"));
		
		return opsForValue.get("test1");
	}
	
	/**
	 * 有时候除了简单的计数外，比如注册用户数需要按日统计，处理方法比较简单，把日期带入计数器 key 就可以。
	 * 以此类推，还可以按其他方式进行统计计数，只需要把统计的方式添加到key值就可以了
	 * @return
	 */
	
	@RequestMapping("/time")
	public String countBytime(){
		
		//将日期添加到key值中
				String key = "test2_" + new SimpleDateFormat("yyyy-MM-dd").format(new Date());
				try {
					ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
					System.out.println(opsForValue.get(key));
					for (int i = 0; i < 100; i++) {
						opsForValue.increment(key, 1);
					}
					System.out.println(opsForValue.get(key));
				} catch (Exception e) {
					e.printStackTrace();
				}
		
		return "";
	}
	
	/*
	 * 场景三：按模糊Key值查询
	 * 
	 * 在按条件统计计数的时候，把时间加入到了key值中，有时候要只是查询某个对象的统计数时，就可以使用模糊Key值查询。
	 * 
	 */
	
	@RequestMapping("/test3")
	public void test3() {
		try {
			ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
			//先获取前缀为test的Key值列表。
			Set<String> keys = stringRedisTemplate.keys("test*");
			//遍历满足条件的Key值获取对应的value值
			for (String a : keys) {
				System.out.println(a + ":" + opsForValue.get(a));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	
	/**
	 * 场景四：设置Key的有效时间（防止高并发访问）
	 * 在redis中可以设置key值的有效时间，用户访问链接的时候，将用户的唯一信息比如ip地址等为key值，时间为value值，在redis中记录一下，
	 * 在用户再次访问的时候，通过key值获取前一次访问的时间，
	 * 比较时间的间隔，如果低于阀值，就拒绝这次请求，防止用户多次访问。这里只是写下在spring的RedisTemplate接口怎么使用。具体的逻辑实现自己搞定。
	 */
	
	// 设置key值的有效时间
		@RequestMapping("/test4")
		public void test4() {
			try {
				ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
				opsForValue.set("test4", "test4");
				System.out.println(opsForValue.get("test4"));
				// TimeUnit.SECONDS:解释定时参数的单位
				// MICROSECONDS 微秒 一百万分之一秒（就是毫秒/1000）
				// MILLISECONDS 毫秒 千分之一秒
				// NANOSECONDS 毫微秒 十亿分之一秒（就是微秒/1000）
				// SECONDS 秒
				// MINUTES 分钟
				// HOURS 小时
				// DAYS 天
				if(stringRedisTemplate.expire("test4", 5, TimeUnit.SECONDS)){
					System.out.println("设置过期时间成功,等待。。。。");
					Thread.sleep(5001);
				}
				System.out.println(opsForValue.get("test4"));
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	
	/**
	 * 场景六：使用lists有序存储读取
	 * 适用于获取最近N个操作的数据。
	 */
		
		//使用lists存储读取 有序(取前三位)
		@RequestMapping("/test6")
		public void test6(){
			ListOperations<String, String> list = stringRedisTemplate.opsForList();
			list.leftPush("test6", "1");
			list.leftPush("test6", "2");
			list.leftPush("test6", "3");
			list.leftPush("test6", "4");
			list.leftPush("test6", "5");
			list.leftPush("test6", "6");
			list.leftPush("test6", "7");
			//保持链表只有3位
			list.trim("test6", 0, 2);
			System.out.println(list.range("test6", 0, list.size("test6")-1));
		}
		
		
		/**
		 * 场景七：使用sets存储读取 无序 去重 求差集，交集，并集
		 * 
		 */
		
		//使用set存储读取  无序 去重  求差集，交集，并集
		@RequestMapping("/test7")
		public void test7(){
			SetOperations<String, String> set = stringRedisTemplate.opsForSet();
			set.add("test7_1", "2", "1","2","3","4","4","3");
			set.add("test7_2", "2", "6","2","3","7","6","5");
			System.out.println("全部成员"+set.members("test7_1"));
			System.out.println("差集"+set.difference("test7_1", "test7_2"));
			System.out.println("交集"+set.intersect("test7_1", "test7_2"));
			System.out.println("并集"+set.union("test7_1", "test7_2"));
		}
	
		
		/**
		 * 场景八：Sorted Set 存取数据 排序
		 * 
		 * 
		 */
		//Sorted Set 存取数据 排序  相比sets 保存时多一个权重参数score，相当于按照此参数来排序
		
		@RequestMapping("/test8")
		public void test8(){
			ZSetOperations<String, String> zSet = stringRedisTemplate.opsForZSet();
			zSet.add("test8", "use1", 9);
			zSet.add("test8", "use2", 1);
			zSet.add("test8", "use3", 5);
			zSet.add("test8", "use4", 9);
			//对应的score值增加
			//zSet.incrementScore("test8", "use1", 1);
			System.out.println(zSet.reverseRange("test8", 0, zSet.size("test8")-1));
		}
}
